
<!DOCTYPE html>
<html lang="zh-Hans" class="loading">
<head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
    <meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <title>Java源码之旅(1) - ArrayList - Luis Blog</title>
    <meta name="apple-mobile-web-app-capable" content="yes" />
    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
    <meta name="google" content="notranslate" />
    <meta name="keywords" content="Luis,"> 
    <meta name="description" content="java开发工程师，喜欢代码，喜欢技术,

 技术在学习中成长，源码的世界没有你想象的那么复杂


前言2018年的五月，开始java的源码学习之旅，从简单的角度去理解java的源码，前几天在学习交流中正好看了一下java集合的源码，才发,"> 
    <meta name="author" content="winter chen"> 
    <link rel="alternative" href="atom.xml" title="Luis Blog" type="application/atom+xml"> 
    <link rel="icon" href="/img/favicon.png"> 
    
    
<link rel="stylesheet" href="/css/diaspora.css">

	<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
    <script>
         (adsbygoogle = window.adsbygoogle || []).push({
              google_ad_client: "ca-pub-8691406134231910",
              enable_page_level_ads: true
         });
    </script>
    <script async custom-element="amp-auto-ads"
        src="https://cdn.ampproject.org/v0/amp-auto-ads-0.1.js">
    </script>
<meta name="generator" content="Hexo 4.2.0"></head>

<body class="loading">
    <span id="config-title" style="display:none">Luis Blog</span>
    <div id="loader"></div>
    <div id="single">
    <div id="top" style="display: block;">
    <div class="bar" style="width: 0;"></div>
    <a class="iconfont icon-home image-icon" href="javascript:;" data-url="https://blog.winterchen.com"></a>
    <div title="播放/暂停" class="iconfont icon-play"></div>
    <h3 class="subtitle">Java源码之旅(1) - ArrayList</h3>
    <div class="social">
        <div>
            <div class="share">
                <a title="获取二维码" class="iconfont icon-scan" href="javascript:;"></a>
            </div>
            <div id="qr"></div>
        </div>
    </div>
    <div class="scrollbar"></div>
</div>

    <div class="section">
        <div class="article">
    <div class='main'>
        <h1 class="title">Java源码之旅(1) - ArrayList</h1>
        <div class="stuff">
            <span>五月 26, 2018</span>
            
  <ul class="post-tags-list" itemprop="keywords"><li class="post-tags-list-item"><a class="post-tags-list-link" href="/tags/java%E6%BA%90%E7%A0%81/" rel="tag">java源码</a></li></ul>


        </div>
        <div class="content markdown">
            <p><img src="http://img.winterchen.com/maximilian-weisbecker-544039-unsplash.jpg" alt=""></p>
<blockquote>
<p> 技术在学习中成长，源码的世界没有你想象的那么复杂</p>
</blockquote>
<a id="more"></a>
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>2018年的五月，开始java的源码学习之旅，从简单的角度去理解java的源码，前几天在学习交流中正好看了一下java集合的源码，才发现源码并没有想象中的那么难以理解，所以，源码之旅从java的集合类开始咯。</p>
<p>本章的源码版本为：<code>JDK1.8</code></p>
<h2 id="类的关系"><a href="#类的关系" class="headerlink" title="类的关系"></a>类的关系</h2><p>要理解<code>ArrayList</code>的源码，我们就需要从它的关系开始，<code>ArrayList</code>继承了<code>AbstractList</code>，实现了<code>List</code>接口，我们从UML图可以看出：</p>
<p><img src="http://img.winterchen.com/WX20180510-154020@2x.png" alt=""></p>
<p>虚线箭头表示实现接口，实线箭头表示继承关系</p>
<h2 id="ArrayList简介"><a href="#ArrayList简介" class="headerlink" title="ArrayList简介"></a>ArrayList简介</h2><p><code>ArrayList</code>是<code>java</code>中最常用的集合类了，说到<code>ArrayList</code>，我们不得不说说<code>LinkedList</code>，因为他们都是从<code>Collection</code>派生而来的，都是用来存放对象的序列的集合类，<code>ArrayList</code>相比与<code>LinkedList</code>有什么优劣呢？</p>
<ul>
<li><p><code>ArrayList</code>:</p>
<ul>
<li><p>优点：<strong>随机访问元素的速度快</strong></p>
</li>
<li><p>缺点：<strong>从中间插入和移除元素比较慢</strong></p>
</li>
</ul>
</li>
<li><p><code>LinkedList</code>：</p>
<ul>
<li><p>优点：<strong>从中间插入和移除元素速度快</strong></p>
</li>
<li><p>缺点：<strong>随机访问元素的速度比较慢</strong></p>
</li>
</ul>
</li>
</ul>
<p>接下来我们就从源码的角度去理解一下为什么有这些优缺点。</p>
<h2 id="源码分析"><a href="#源码分析" class="headerlink" title="源码分析"></a>源码分析</h2><h3 id="ArrayList的初始化"><a href="#ArrayList的初始化" class="headerlink" title="ArrayList的初始化"></a>ArrayList的初始化</h3><p><code>ArrayList</code>有三个构造方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//默认初始容量</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> DEFAULT_CAPACITY = <span class="number">10</span>;</span><br><span class="line"><span class="comment">//空的元素数组</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> Object[] EMPTY_ELEMENTDATA = &#123;&#125;;</span><br><span class="line"><span class="comment">//初始容量空的元素数组</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = &#123;&#125;;</span><br><span class="line"><span class="comment">// 存放元素的数组</span></span><br><span class="line"><span class="keyword">transient</span> Object[] elementData; <span class="comment">// non-private to simplify nested class access</span></span><br><span class="line"><span class="comment">//数组的大小</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">int</span> size;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 空参的构造方法</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">ArrayList</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; <span class="comment">// -- (1)</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//指定初始容量的构造方法</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">ArrayList</span><span class="params">(<span class="keyword">int</span> initialCapacity)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (initialCapacity &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">this</span>.elementData = <span class="keyword">new</span> Object[initialCapacity]; <span class="comment">// -- (2)</span></span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (initialCapacity == <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">this</span>.elementData = EMPTY_ELEMENTDATA; <span class="comment">// -- (3)</span></span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">"Illegal Capacity: "</span>+</span><br><span class="line">                                               initialCapacity);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//初始化给定一个集合的构造函数</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">ArrayList</span><span class="params">(Collection&lt;? extends E&gt; c)</span> </span>&#123;</span><br><span class="line">        elementData = c.toArray();    <span class="comment">// -- (4)</span></span><br><span class="line">        <span class="keyword">if</span> ((size = elementData.length) != <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="comment">// c.toArray might (incorrectly) not return Object[] (see 6260652)</span></span><br><span class="line">            <span class="keyword">if</span> (elementData.getClass() != Object[].class)</span><br><span class="line">                elementData = Arrays.copyOf(elementData, size, Object[].class);  <span class="comment">// -- (5)</span></span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// replace with empty array.</span></span><br><span class="line">            <span class="keyword">this</span>.elementData = EMPTY_ELEMENTDATA;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<p>从源码中我们可以看出，<code>ArrayList</code>其实底层使用的是<code>数组</code>，<code>transient Object[] elementData</code> 这个变量就是用来存放对象的一个数组。</p>
<ul>
<li><p><code>ArrayList</code>的空参构造函数：也就是默认的构造函数，当<code>new ArrayList()</code>的时候调用这个方法，可以看出将<code>elementData</code>变量地址指向了<code>DEFAULTCAPACITY_EMPTY_ELEMENTDATA</code>这个<strong>初始容量为10，并且为空的元素数组</strong>;如步骤<code>(1)</code></p>
</li>
<li><p><code>ArrayList</code>的指定大小的构造函数：当初始化一个指定大小的<code>new ArrayList(int)</code>的时候调用该方法，这个方法首先对<code>initialCapacity</code>参数进行判断，<strong>如果大于0</strong>，那么创建一个指定大小的数组<code>(2)</code>；<strong>如果等于0</strong>,创建一个空的数组<code>(3)</code>；否则就判处异常；</p>
</li>
<li><p>初始化给定一个集合的构造函数：如果初始化的时候，给定一个集合对象，那么将这个集合转换为数组 <code>(4)</code>，然后对这个数组的长度进行判断，如果数组不等于0，那么调用<code>Arrays.copyOf(elementData, size, Object[].class)</code>方法<code>(5)</code>，这个方法是一个<strong>核心方法</strong>，这个方法就是初始化一个大小为等于当前数组的一个新的数组，然后将对象<code>copy</code>到新的数组中，然后将内存地址指定给<code>elementData</code>，从下面的<code>Arrays.copyOf</code>的源码可以看出来。</p>
</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> &lt;T,U&gt; T[] copyOf(U[] original, <span class="keyword">int</span> newLength, Class&lt;? extends T[]&gt; newType) &#123;</span><br><span class="line">        <span class="meta">@SuppressWarnings</span>(<span class="string">"unchecked"</span>)</span><br><span class="line">        T[] copy = ((Object)newType == (Object)Object[].class)</span><br><span class="line">            ? (T[]) <span class="keyword">new</span> Object[newLength]</span><br><span class="line">            : (T[]) Array.newInstance(newType.getComponentType(), newLength);</span><br><span class="line">        System.arraycopy(original, <span class="number">0</span>, copy, <span class="number">0</span>,</span><br><span class="line">                         Math.min(original.length, newLength));</span><br><span class="line">        <span class="keyword">return</span> copy;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<p><code>copyOf</code>方法首先判断两个对象的类型，如果类型一致，那么直接创建一个同大小的数组；如果类型不一致，则调用<code>Array.newInstance</code>指定类型进行初始化这个数组，当然，大小也是一致的; 最后调用<code>System.arraycopy</code>将参数数组<code>copy</code>到新的目标并返回。</p>
<h3 id="ArrayList的常用方法之-add"><a href="#ArrayList的常用方法之-add" class="headerlink" title="ArrayList的常用方法之 add"></a>ArrayList的常用方法之 add</h3><p>我们先看一下源码：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">add</span><span class="params">(E e)</span> </span>&#123;</span><br><span class="line">        ensureCapacityInternal(size + <span class="number">1</span>);  <span class="comment">// Increments modCount!! -- (1)</span></span><br><span class="line">        elementData[size++] = e;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">add</span><span class="params">(<span class="keyword">int</span> index, E element)</span> </span>&#123;</span><br><span class="line">        rangeCheckForAdd(index);<span class="comment">//-- (2)</span></span><br><span class="line"></span><br><span class="line">        ensureCapacityInternal(size + <span class="number">1</span>);  <span class="comment">// Increments modCount!!</span></span><br><span class="line">        System.arraycopy(elementData, index, elementData, index + <span class="number">1</span>,</span><br><span class="line">                         size - index); <span class="comment">//-- (3)</span></span><br><span class="line">        elementData[index] = element;</span><br><span class="line">        size++;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<p>ArrayList有两个add方法，第一个方法就是按顺序将对象插入到尾部，第二个方法就是从中间插入对象。</p>
<p><code>add(E e)</code> 方法首先会判断数组的容量是否超过极限<code>ensureCapacityInternal(size + 1)</code>，这个方法首先会进行容量的判断，如果超过了极限，创建一个新的数组，大小是旧数组1.5倍，然后将旧数组中的对象全部拷贝到新的数组<code>(1)</code>，等下会详细解析这个方法。最后将参数对象插入到数组中，返回true。</p>
<p><code>add(int index, E element)</code>首先会调用<code>rangeCheckForAdd(index)</code>进行index的是否越界的验证<code>(2)</code>，然后调用上一个方法中一样的判断容量是否超过极限的方法，下一步就是一个核心的方法<code>System.arraycopy</code>，这个方法我们在<code>ArrayList初始化中</code>已经讲过了，但是这里不太一样：</p>
<ul>
<li><p><code>elementData</code> : 源数组</p>
</li>
<li><p><code>index</code>：源数组起始位置</p>
</li>
<li><p><code>elementData</code>：目标数组</p>
</li>
<li><p><code>index + 1</code>：目标数组起始位置</p>
</li>
<li><p><code>size - index</code>：复制数组元素数目</p>
</li>
</ul>
<p>从源码中可以看出，当我们往一个ArrayList中间插入一个对象的时候，index索引处后面的索引往后移动一位，最后把索引为index空出来，并将element赋值给它。这样一来我们并不知道要插入哪个位置，所以会进行匹配那么它的时间赋值度就为n。</p>
<p>接下来看一下<code>ensureCapacityInternal(size + 1)</code>这个方法的调用链：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">ensureCapacityInternal</span><span class="params">(<span class="keyword">int</span> minCapacity)</span> </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) &#123;</span><br><span class="line">    minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  ensureExplicitCapacity(minCapacity);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">ensureExplicitCapacity</span><span class="params">(<span class="keyword">int</span> minCapacity)</span> </span>&#123;</span><br><span class="line">  modCount++;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// overflow-conscious code</span></span><br><span class="line">  <span class="keyword">if</span> (minCapacity - elementData.length &gt; <span class="number">0</span>)</span><br><span class="line">    grow(minCapacity);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">grow</span><span class="params">(<span class="keyword">int</span> minCapacity)</span> </span>&#123;</span><br><span class="line">  <span class="comment">// overflow-conscious code</span></span><br><span class="line">  <span class="keyword">int</span> oldCapacity = elementData.length;</span><br><span class="line">  <span class="keyword">int</span> newCapacity = oldCapacity + (oldCapacity &gt;&gt; <span class="number">1</span>);<span class="comment">//oldCapacity &gt;&gt; 1 就是除以2</span></span><br><span class="line">  <span class="keyword">if</span> (newCapacity - minCapacity &lt; <span class="number">0</span>)</span><br><span class="line">    newCapacity = minCapacity;</span><br><span class="line">  <span class="keyword">if</span> (newCapacity - MAX_ARRAY_SIZE &gt; <span class="number">0</span>)</span><br><span class="line">    newCapacity = hugeCapacity(minCapacity);</span><br><span class="line">  <span class="comment">// minCapacity is usually close to size, so this is a win:</span></span><br><span class="line">  elementData = Arrays.copyOf(elementData, newCapacity);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>ensureCapacityInternal(int minCapacity)</code>方法中判断当前数组中的元素是否为空，如果为空则给定一个最大的值，然后调用<code>ensureExplicitCapacity(minCapacity)</code>，这个方法主要是判数组容量是否超过极限，如果超过极限调用<code>grow(int minCapacity)</code>，这个方法就是扩容方法，该方法会创建一个比原数组大1.5倍的新数组，然后将原数组中的所有对象<code>copy</code>到新的数组中。</p>
<h3 id="ArrayList的常用方法之-remove"><a href="#ArrayList的常用方法之-remove" class="headerlink" title="ArrayList的常用方法之 remove"></a>ArrayList的常用方法之 remove</h3><p><code>remove</code>方法其实跟从中间插入对象的<code>add</code>方法有很大的相似之处，如果我们删除某一个元素，将<code>index</code>开始后面的所有对象都往前移动一位，底层方法其实是复制一遍，所以删除一个对象的复杂度和从中间插入一个对象是差不多的。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> E <span class="title">remove</span><span class="params">(<span class="keyword">int</span> index)</span> </span>&#123;</span><br><span class="line">        rangeCheck(index);</span><br><span class="line"></span><br><span class="line">        modCount++;</span><br><span class="line">        E oldValue = elementData(index);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">int</span> numMoved = size - index - <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">if</span> (numMoved &gt; <span class="number">0</span>)</span><br><span class="line">            System.arraycopy(elementData, index+<span class="number">1</span>, elementData, index,</span><br><span class="line">                             numMoved);</span><br><span class="line">        elementData[--size] = <span class="keyword">null</span>; <span class="comment">// clear to let GC do its work</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> oldValue;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<h3 id="ArrayList的常用方法之-get"><a href="#ArrayList的常用方法之-get" class="headerlink" title="ArrayList的常用方法之 get"></a>ArrayList的常用方法之 get</h3><p><code>ArrayList</code>的<code>get</code>方法非常直观的就能理解了，废话不多说，直接看代码:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> E <span class="title">get</span><span class="params">(<span class="keyword">int</span> index)</span> </span>&#123;</span><br><span class="line">  rangeCheck(index);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> elementData(index);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>检查index合法性，然后从数组中取出对象并返回，是不是很简单呢？</p>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>本章列举了一些ArrayList常用的方法，了解到ArrayList底层其实是一个对象数组，以及从中间插入对象和移除对象比较慢的原因，从这些方法出发，理解ArrayList其他的方法会很简单了。下一章讲一讲LinkedList的源码。</p>

            <!--[if lt IE 9]><script>document.createElement('audio');</script><![endif]-->
            <audio id="audio" loop="1" preload="auto" controls="controls" data-autoplay="false">
                <source type="audio/mpeg" src="">
            </audio>
            
                <ul id="audio-list" style="display:none">
                    
                        
                            <li title='0' data-url='https://raw.githubusercontent.com/WinterChenS/imgrpo/master/mp3/%E6%96%B0%E8%A3%A4%E5%AD%90-%E7%94%9F%E6%B4%BB%E5%9B%A0%E4%BD%A0%E8%80%8C%E7%81%AB%E7%83%AD.mp3'></li>
                        
                    
                </ul>
            
        </div>
        
    <div id='gitalk-container' class="comment link"
		data-enable='false'
        data-ae='false'
        data-ci='77f2adc33e0112d086d7'
        data-cs='e926aa3a621556609b2c2b76d89320fa524e23cd'
        data-r='WinterChenS.github.io'
        data-o='WinterChenS'
        data-a='WinterChenS'
        data-d='false'
    >查看评论</div>


    </div>
    
</div>


    </div>
</div>
</body>


<script src="//lib.baomitu.com/jquery/1.8.3/jquery.min.js"></script>
<script src="/js/plugin.js"></script>
<script src="/js/typed.js"></script>
<script src="/js/diaspora.js"></script>


<link rel="stylesheet" href="/photoswipe/photoswipe.css">
<link rel="stylesheet" href="/photoswipe/default-skin/default-skin.css">


<script src="/photoswipe/photoswipe.min.js"></script>
<script src="/photoswipe/photoswipe-ui-default.min.js"></script>


<!-- Root element of PhotoSwipe. Must have class pswp. -->
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">
    <!-- Background of PhotoSwipe. 
         It's a separate element as animating opacity is faster than rgba(). -->
    <div class="pswp__bg"></div>
    <!-- Slides wrapper with overflow:hidden. -->
    <div class="pswp__scroll-wrap">
        <!-- Container that holds slides. 
            PhotoSwipe keeps only 3 of them in the DOM to save memory.
            Don't modify these 3 pswp__item elements, data is added later on. -->
        <div class="pswp__container">
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
        </div>
        <!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
        <div class="pswp__ui pswp__ui--hidden">
            <div class="pswp__top-bar">
                <!--  Controls are self-explanatory. Order can be changed. -->
                <div class="pswp__counter"></div>
                <button class="pswp__button pswp__button--close" title="Close (Esc)"></button>
                <button class="pswp__button pswp__button--share" title="Share"></button>
                <button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>
                <button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>
                <!-- Preloader demo http://codepen.io/dimsemenov/pen/yyBWoR -->
                <!-- element will get class pswp__preloader--active when preloader is running -->
                <div class="pswp__preloader">
                    <div class="pswp__preloader__icn">
                      <div class="pswp__preloader__cut">
                        <div class="pswp__preloader__donut"></div>
                      </div>
                    </div>
                </div>
            </div>
            <div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
                <div class="pswp__share-tooltip"></div> 
            </div>
            <button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
            </button>
            <button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
            </button>
            <div class="pswp__caption">
                <div class="pswp__caption__center"></div>
            </div>
        </div>
    </div>
</div>





<!-- Google Analytics -->
<script type="text/javascript">
  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');

  ga('create', 'G-30C04D6TMS', 'auto');
  ga('send', 'pageview');
</script>
<!-- End Google Analytics -->


</html>
